﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;
using System.Xml.Linq;
using WSManager.Model;
using WSManager.Logic.Transformation;
using StringVariables;
using WSManager.ModelValidation;

namespace WSManager.Wpf.ArticleEditor
{
    public class EditorViewModel : ViewModelBase<Article>
    {
        protected Web Context;
        private string _fileName;
        public Article Article
        {
            get { return Item; }
            set
            {
                Item = value;
            }
        }
        private bool _loading;
        readonly List<WebFile> _filesToAdd = new List<WebFile>();
        readonly List<WebImage> _imagesToAdd = new List<WebImage>();
        readonly ArticleToXamlTransformer _toXaml = new ArticleToXamlTransformer();


        public string Name
        {
            get { return Item.Name; }
            set
            {
                Item.Name = value;
                RaisePropertyChanged("Name");
            }
        }

        public string Description
        {
            get { return Item.Description; }
            set
            {
                Item.Description = value;
                RaisePropertyChanged("Description");
            }
        }

        public bool Loading
        {
            get { return _loading; }
            set
            {
                _loading = value;
                RaisePropertyChanged("Loading");
            }
        }

        public EditorViewModel()
        {
            Article = ArticleEditorManager.RegisterEditorVm(this);
            Context = ArticleEditorManager.GetWebContext(this);
            foreach (var item in Article.Categories)
            {
                Categories.FindCategory(item.Id).Checked = true;
            }
        }


        public XElement Content
        {
            get { return Article.Content ?? (Article.Content = new XElement("content")); }
            set
            {
                if (Article.Content != value)
                {
                    Article.Content = value;
                    RaisePropertyChanged("Content");
                    RaisePropertyChanged("XamlContent");
                }
            }
        }

        public DependencyObject XamlContent
        {
            get
            {
                var str = this._toXaml.ConvertXml(new XElement(this.Content));
                var depo = XamlReader.Parse(str) as DependencyObject;
                return depo;
            }
        }

        public string FileName
        {
            get { return _fileName; }
            set
            {
                _fileName = value;
                RaisePropertyChanged("FileName");
            }
        }

        public bool MetadataValid
        {
            get { return !Context.Metadata.ArticleMetadataDefinition.Validate(Item.Metadata).GetEnumerator().MoveNext(); }
        }

        public void SaveArticle(Window window)
        {
            if (!MetadataValid)
            {
                MessageBox.Show("Metadata are not valid");
                EditMetadata();
                return;
            }
            Article.Categories = GetSelectedCategories().Select(c => c.Category).ToList();
            if (!new ArticleValidator().Validate(Article).IsValid)
            {
                MessageBox.Show("article is not valid");
                return;
            }
            SaveExternalContent();
            ArticleEditorManager.SaveArticle(this);
            window.Close();
        }

        private void SaveExternalContent()
        {
            Context.Files = Context.Files.Concat(GetLoadedFiles());
            Context.Images = Context.Images.Concat(GetLoadedImages());
        }

        private IEnumerable<WebImage> GetLoadedImages()
        {
            return from image in _imagesToAdd
                   let file = new FileInfo(image.Path)
                   where file.Exists
                   select image.Load();
        }

        private IEnumerable<WebFile> GetLoadedFiles()
        {
            return from file in _filesToAdd
                   let fileInfo = new FileInfo(file.Path)
                   where fileInfo.Exists
                   select file.Load();
        }

        public void LoadFile()
        {
            Loading = true;
            ThreadPool.QueueUserWorkItem(d =>
            {
                try
                {
                    var stream = File.OpenRead(FileName);
                    var convertedXml = Logic.Transformation.Articles.ArticleTransformerFactory.Html().ConvertStream(stream);
                    Content = convertedXml.Element("content");
                    if (convertedXml.Element("meta").Attribute("name") != null)
                    {
                        Name = convertedXml.Element("meta").Attribute("name").Value;
                    }
                    _filesToAdd.Clear();
                    foreach (var item in convertedXml.Element("files").Elements("file"))
                    {
                        AddFile(item);
                    }
                    _imagesToAdd.Clear();
                    foreach (var item in convertedXml.Element("images").Elements("image"))
                    {
                        AddImage(item);
                    }
                }
                catch (NotSupportedException ex)
                {
                    MessageBox.Show("File format not supported");
#if DEBUG
                    System.Diagnostics.Debugger.Break();
#endif
                }
                catch (FileNotFoundException ex)
                {
                    MessageBox.Show("File not found");
#if DEBUG
                    System.Diagnostics.Debugger.Break();
#endif
                }
                finally
                {
                    Loading = false;
                }
            });
        }

        private void AddFile(XElement fileElement)
        {
            string filePath = (string)fileElement.Attribute("src");
            if (filePath == null) return;
            if (!Path.IsPathRooted(filePath))
            {
                filePath = Path.Combine(Path.GetDirectoryName(this.FileName), path2: filePath);
            }
            _filesToAdd.Add(
                new WebFile
                {
                    Name = (string)fileElement.Attribute("name"),
                    Path = filePath
                });
        }

        private void AddImage(XElement imageElement)
        {
            string imagePath = (string)imageElement.Attribute("src");
            if (imagePath == null) return;
            if (!Path.IsPathRooted(imagePath))
            {
                imagePath = Path.Combine(Path.GetDirectoryName(this.FileName), imagePath).Replace('\\', '/');
            }
            imageElement.Attribute("src").Value = imagePath;
            _imagesToAdd.Add(new WebImage { Path = imagePath });
        }

        #region Commands
        public ICommand FileDialog { get { return new RelayCommand(OpenFDialog); } }
        public ICommand Save { get { return new RelayCommand<Window>(SaveArticle); } }
        public ICommand EditMeta { get { return new RelayCommand(EditMetadata); } }

        public void EditMetadata()
        {
            Dialogs.EditVariablesDialog.Show(Context.Metadata.ArticleMetadataDefinition, Article.Metadata, s=> RaisePropertyChanged("MetadataValid"));
        }

        public void OpenFDialog()
        {
            var dialog = new Microsoft.Win32.OpenFileDialog();
            dialog.FileName = FileName;
            if (dialog.ShowDialog() == true)
            {
                FileName = dialog.FileName;
                LoadFile();
            }
        }

        public ICommand FileLoad { get { return new RelayCommand(LoadFile); } }


        #endregion

        #region Categories
        public bool CategoriesSelecting { get; private set; }
        List<CategoryViewModel> _categories;
        public List<CategoryViewModel> Categories
        {
            get
            {
                if (_categories == null)
                {
                    _categories = Context.Categories.Select(c => new CategoryViewModel(c) { CheckReferenced = false }).ToList();
                }
                return _categories;
            }
        }
        public string SelectedCategoriesString
        {
            get
            {
                return string.Join(", ", GetSelectedCategories().Select(c => c.Name));
            }
        }

        public ICommand ToggleCategoriesSelectingCommand { get { return new RelayCommand(ToggleCategoriesSelecting); } }


        void ToggleCategoriesSelecting()
        {
            CategoriesSelecting = !CategoriesSelecting;
            RaisePropertyChanged("CategoriesSelecting");
            if (!CategoriesSelecting)
            {
                RaisePropertyChanged("SelectedCategoriesString");
            }
        }

        IEnumerable<CategoryViewModel> GetSelectedCategories(IEnumerable<CategoryViewModel> categories = null)
        {
            if (categories == null)
                categories = this.Categories;
            foreach (var item in categories)
            {
                if (item.Checked == true)
                    yield return item;
                if (item.Subcategories.Count > 0)
                    foreach (var i in GetSelectedCategories(item.Subcategories))
                        yield return i;
            }
        }



        #endregion
    }
}
